use crate::errors::map_rmpv_write_err;
use crate::param::PersisParams;
use crate::protocols::{Transport, TransportRead, TransportType, TransportWrite};
use crate::util::from_slice_with_null_to_string;
use crate::Result;
use alloc::string::String;
use alloc::vec::Vec;
use rmpv::encode::write_value;
use rmpv::{Utf8String, Value};

#[derive(Debug, Clone)]
pub struct SysInfo {
    pub base: BaseInfo,
    pub net: NetInfo,
    pub ver: VerInfo,
    pub wifi: WifiInfo,
    pub battery: BatteryInfo,
}
impl SysInfo {
    pub fn get() -> SysInfo {
        unsafe {
            if INFO.is_none() {
                let param = PersisParams::get();
                let info = SysInfo {
                    base: BaseInfo {
                        device_uid: from_slice_with_null_to_string(&param.device_uid),
                        product_code: from_slice_with_null_to_string(&param.product_code),
                        device_sn: from_slice_with_null_to_string(&param.device_sn),
                    },
                    net: NetInfo {
                        ip: String::from("192.168.1.103"),
                        gw: String::from("192.168.1.1"),
                        dns: String::from("8.8.8.8"),
                        netmask: String::from("255.255.255.0"),
                    },
                    ver: VerInfo {
                        sys: String::from("1.0.0"),
                        sdk: String::from(env!("CARGO_PKG_VERSION")),
                        mcu: String::from("1.0.0"),
                        hw: String::from("1.0.0"),
                    },
                    wifi: WifiInfo {
                        mac: String::from("00:11:22:33:44:55"),
                        ssid: String::from("TL-Song-n"),
                        bssid: String::from("11:22:33:44:55:66"),
                        level: 50,
                        rssi: String::from("-30dbm"),
                    },
                    battery: BatteryInfo {
                        cap: 4000,
                        act: 3900,
                        sn: String::from("FOX111000000"),
                    },
                };
                INFO = Some(info);
            }
            INFO.as_ref().unwrap().clone()
        }
    }

    pub fn update(info: SysInfo) {
        unsafe { INFO = Some(info) }
    }
}

static mut INFO: Option<SysInfo> = None;
impl Transport for SysInfo {
    fn serialize(&self, _tt: TransportType, buff: &mut dyn TransportWrite) -> Result<()> {
        let mut root: Vec<(Value, Value)> = Vec::new();
        let writer = buff.payload();
        root.push((
            Value::String(Utf8String::from("base")),
            self.base.to_value(),
        ));
        root.push((Value::String(Utf8String::from("vers")), self.ver.to_value()));
        root.push((Value::String(Utf8String::from("net")), self.net.to_value()));
        root.push((
            Value::String(Utf8String::from("wifi")),
            self.wifi.to_value(),
        ));
        root.push((
            Value::String(Utf8String::from("battery")),
            self.battery.to_value(),
        ));
        write_value(writer, &Value::Map(root)).map_err(map_rmpv_write_err)
    }

    fn deserialize(&self, _tt: TransportType, _buff: &dyn TransportRead) -> Result<()> {
        todo!()
    }
}

#[derive(Debug, Clone)]
pub struct BaseInfo {
    pub device_uid: String,
    pub product_code: String,
    pub device_sn: String,
}

impl BaseInfo {
    pub fn to_value(&self) -> Value {
        let mut info: Vec<(Value, Value)> = Vec::new();
        info.push((
            Value::String(Utf8String::from("did")),
            Value::String(Utf8String::from(self.device_uid.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("prod_code")),
            Value::String(Utf8String::from(self.product_code.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("sn")),
            Value::String(Utf8String::from(self.device_sn.as_str())),
        ));
        Value::Map(info)
    }
}

#[derive(Debug, Clone)]
pub struct NetInfo {
    pub ip: String,
    pub gw: String,
    pub dns: String,
    pub netmask: String,
}

impl NetInfo {
    pub fn to_value(&self) -> Value {
        let mut info: Vec<(Value, Value)> = Vec::new();
        info.push((
            Value::String(Utf8String::from("ip")),
            Value::String(Utf8String::from(self.ip.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("gw")),
            Value::String(Utf8String::from(self.gw.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("dns")),
            Value::String(Utf8String::from(self.dns.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("netmask")),
            Value::String(Utf8String::from(self.netmask.as_str())),
        ));
        Value::Map(info)
    }
}

#[derive(Debug, Clone)]
pub struct VerInfo {
    pub sys: String,
    pub sdk: String,
    pub mcu: String,
    pub hw: String,
}

impl VerInfo {
    pub fn to_value(&self) -> Value {
        let mut info: Vec<(Value, Value)> = Vec::new();
        info.push((
            Value::String(Utf8String::from("sys")),
            Value::String(Utf8String::from(self.sys.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("sdk")),
            Value::String(Utf8String::from(self.sdk.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("mcu")),
            Value::String(Utf8String::from(self.mcu.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("hw")),
            Value::String(Utf8String::from(self.hw.as_str())),
        ));
        Value::Map(info)
    }
}

#[derive(Debug, Clone)]
pub struct WifiInfo {
    pub mac: String,
    pub ssid: String,
    pub bssid: String,
    pub level: u8,
    pub rssi: String,
}

impl WifiInfo {
    pub fn to_value(&self) -> Value {
        let mut info: Vec<(Value, Value)> = Vec::new();
        info.push((
            Value::String(Utf8String::from("mac")),
            Value::String(Utf8String::from(self.mac.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("ssid")),
            Value::String(Utf8String::from(self.ssid.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("bssid")),
            Value::String(Utf8String::from(self.bssid.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("level")),
            Value::Integer(self.level.into()),
        ));
        info.push((
            Value::String(Utf8String::from("rssi")),
            Value::String(Utf8String::from(self.rssi.as_str())),
        ));
        Value::Map(info)
    }
}

#[derive(Debug, Clone)]
pub struct BatteryInfo {
    pub cap: u32,
    pub act: u32,
    pub sn: String,
}
impl BatteryInfo {
    pub fn to_value(&self) -> Value {
        let mut info: Vec<(Value, Value)> = Vec::new();
        info.push((
            Value::String(Utf8String::from("sn")),
            Value::String(Utf8String::from(self.sn.as_str())),
        ));
        info.push((
            Value::String(Utf8String::from("cap")),
            Value::Integer(self.cap.into()),
        ));
        info.push((
            Value::String(Utf8String::from("act")),
            Value::Integer(self.act.into()),
        ));
        Value::Map(info)
    }
}
